#pragma once

#include <list>
#include <cstdint>
#include <functional>
#include <string>
#include <vector>
#include "../util/allocator.hh"
#include <algorithm>

namespace kratos { namespace corelib {

class ConsistentHash;

// Callback for hash circle changed
// The key must transfer from oldNode to newNode
using RehashCallback = std::function<void(std::uint64_t oldNode, std::uint64_t newNode)>;
// Hash method from std::string to std::uiint64_t
using StringHashMethod = std::function<std::uint32_t(const std::string&)>;

enum class HashType {
    HASH_TYPE_MOD = 1,
    HASH_TYPE_CIRCLE,
};

// The hash circle(ring) of consistent hashing
class HashCircle {
    struct HashNode {
        std::uint32_t key; // The key of node
        std::uint64_t value; // user value
    };
    // for lower_bound
    static bool compare(const HashNode& a, const uint64_t key)  {
        if (a.key < key) {
            return true;
        } else {
            return false;
        }
    }
    using Circle = std::vector<HashNode>;
    Circle circle_; // The ring
    using ValueMethod = std::function<void(std::uint64_t)>;

public:
    // Add node to ring
    // @param key key in ring
    // @param value user value
    // @retval true
    // @retval false fail
    bool addNode(std::uint32_t key, std::uint64_t value, ConsistentHash& hash);
    // Remove node from ring
    // @param key key in ring
    // @retval true
    // @retval false fail
    bool removeNode(std::uint32_t key, ConsistentHash& hash);
    // Query a node value for given key
    // @param key key
    // @param method The method to return value
    // @param type hash type @see HashType
    // @retval true
    // @retval false fail
    bool findNode(std::uint64_t key, ValueMethod method, HashType type = HashType::HASH_TYPE_CIRCLE);
};

// Consistent hashing
class ConsistentHash {
    HashCircle circle_;
    using NodeMap = std::unordered_map<std::string, std::size_t>;
    NodeMap nodeMap_; // node map
    StringHashMethod hashMethod_; // Hashing method from std::string to std::uint64_t
    RehashCallback rehashCallback_; // rehash method for consistent circle changed
    bool start_; // Startup flag

public:
    // ctor
    ConsistentHash();
    // dtor
    ~ConsistentHash();
    // Startup
    // @param rehashCallback rehash method for consistent circle changed
    // @param hashMethod string hash method
    // @retval true
    // @retval false fail
    bool start(RehashCallback rehashCallback = nullptr, StringHashMethod hashMethod = nullptr);
    // Add virtual node
    // @param name node name
    // @param value The value of node
    // @param count The virtual node count for the real node
    // @retval true
    // @retval false fail
    bool addNode(const std::string& name, std::uint64_t value, std::size_t count = 32);
    // Add virtual node
    // @param key node key
    // @param value The value of node
    // @param count The virtual node count for the real node
    // @retval true
    // @retval false fail
    bool addNode(std::uint64_t key, std::uint64_t value, std::size_t count = 32);
    // Remove virtual node
    // @param name node name
    // @retval true
    // @retval false fail
    bool removeNode(const std::string& name);
    // Remove virtual node
    // @param key node key
    // @retval true
    // @retval false fail
    bool removeNode(std::uint64_t key);
    // Returns a node value by hashing
    // @param key The key needs to hash
    // @param type hash type @see HashType
    // @retval 0 fail
    // @retval non-zero The node value
    std::uint64_t getNode(std::uint64_t key, HashType type = HashType::HASH_TYPE_CIRCLE);
    // Returns virtual node count
    // @param name node name
    // @return virtual node count
    std::size_t getVNodeCount(const std::string& name);

private:
    // Rehash key from oldNode to newNode
    void rehash(std::uint64_t oldNode, std::uint64_t newNode);

    friend class HashCircle;
};

}}
